part of simple_state_driver;

class UpdatableBuilder extends StatefulWidget {
  const UpdatableBuilder({required this.builder, Key? key, this.extraUpdater})
      : _updatableState = null,
        _widgetBuilder = null,
        _internal = false,
        super(key: key);
  const UpdatableBuilder._(this._updatableState, this._widgetBuilder,
      {Key? key, this.extraUpdater})
      : builder = null,
        _internal = true,
        super(key: key);
  final UpdateBuilder? builder;
  final Updater? extraUpdater;
  final UpdatableMixin? _updatableState;
  final WidgetBuilder? _widgetBuilder;
  final bool _internal;
  @override
  State createState() => _UpdatableBuilderState();
}

class _UpdatableBuilderState extends State<UpdatableBuilder> {
  @override
  Widget build(BuildContext context) => widget._internal
      ? widget._widgetBuilder!(context)
      : widget.builder!(context, update);
  @override
  void initState() {
    super.initState();
    widget._updatableState?.addUpdater(update);
  }

  @override
  void dispose() {
    widget._updatableState?.removeUpdater(update);
    super.dispose();
  }

  void update() {
    // (context as Element?)?.markNeedsBuild();
    if (mounted) setState(() {});
    widget.extraUpdater?.call();
  }
}

class UpdatablesBuilder extends StatefulWidget {
  const UpdatablesBuilder({
    Key? key,
    required this.updatableStates,
    required this.builder,
    this.extraUpdater,
  }) : super(key: key);
  final List<UpdatableMixin> updatableStates;
  final WidgetBuilder builder;
  final Updater? extraUpdater;
  @override
  State createState() => _UpdatablesBuilderState();
}

class _UpdatablesBuilderState extends State<UpdatablesBuilder> {
  @override
  Widget build(BuildContext context) => widget.builder(context);
  @override
  void initState() {
    super.initState();
    for (final updatableState in widget.updatableStates) {
      updatableState.addUpdater(update);
    }
  }

  @override
  void dispose() {
    for (final updatableState in widget.updatableStates) {
      updatableState.removeUpdater(update);
    }
    super.dispose();
  }

  void update() {
    // (context as Element?)?.markNeedsBuild();
    if (mounted) setState(() {});
    widget.extraUpdater?.call();
  }
}

class SimpleUpdateBuilder<T extends SimpleStateController>
    extends StatefulWidget {
  const SimpleUpdateBuilder(
      {Key? key,
      required this.builder,
      this.stateController,
      this.componentKey,
      this.stateKeys = const [],
      this.getComponentKey,
      this.getStateKeys,
      this.tag})
      : super(key: key);
  final Widget Function(BuildContext context, T data) builder;
  final T? stateController;
  final String? componentKey;
  final List<Object> stateKeys;
  final String Function(T stateController)? getComponentKey;
  final List<Object> Function(T stateController)? getStateKeys;
  final String? tag;
  @override
  State<SimpleUpdateBuilder<T>> createState() => SimpleUpdateBuilderState<T>();
}

class SimpleUpdateBuilderState<T extends SimpleStateController>
    extends State<SimpleUpdateBuilder<T>> {
  static int componentID = 0;
  late T stateController;
  late String? componentKey;
  late List<Object> stateKeys;
  @override
  Widget build(BuildContext context) {
    return widget.builder(context, stateController);
  }

  @override
  void initState() {
    super.initState();
    componentID++;
    stateController =
        widget.stateController ?? SimpleStateManager.find<T>(tag: widget.tag);
    componentKey =
        widget.componentKey ?? widget.getComponentKey?.call(stateController);
    stateKeys = widget.getStateKeys?.call(stateController) ?? widget.stateKeys;
    stateKeys = stateController.initStateKeys(stateKeys);
    stateController.addUpdater(componentKey, stateKeys, update);
  }

  @override
  void dispose() {
    stateController.removeUpdater(componentKey, stateKeys, update);
    super.dispose();
  }

  void update() {
    if (mounted) setState(() {});
    // (context as Element?)?.markNeedsBuild();
    // if (mounted) {
    //   setState(() {
    //     // print('callSetState');
    //   });
    // }
  }
}

class StateUpdateBuilder<T, S extends AbstractState<T>> extends StatefulWidget {
  const StateUpdateBuilder({
    required this.state,
    required this.builder,
    this.shouldUpdate,
    this.relyValue,
    this.extraUpdater,
    this.child,
    super.key,
  });
  final S state;
  final ValueBuilder<T> builder;
  final ShouldUpdate<T>? shouldUpdate;
  final T? relyValue;
  final Updater? extraUpdater;
  final Widget? child;

  @override
  State<StateUpdateBuilder<T, S>> createState() =>
      _StateUpdateBuilderState<T, S>();
}

class _StateUpdateBuilderState<T, S extends AbstractState<T>>
    extends State<StateUpdateBuilder<T, S>> with SingleStateUpdateMixin {
  @override
  Widget build(BuildContext context) {
    return widget.builder(context, widget.state.value, widget.child);
  }

  @override
  bool shouldUpdate(newValue, oldValue) {
    if (widget.shouldUpdate != null) {
      return widget.shouldUpdate!(newValue, oldValue);
    }
    final relyValue = widget.relyValue;
    if (relyValue != null) {
      return relyValue == newValue || relyValue == oldValue;
    }
    return true;
  }

  @override
  AbstractState get state => widget.state;

  @override
  void _update() {
    if (mounted) setState(() {});
    // (context as Element?)?.markNeedsBuild();
    widget.extraUpdater?.call();
  }
}

class StateUpdateDelegate<T, S extends AbstractState<T>>
    extends StatefulWidget {
  const StateUpdateDelegate({
    required this.state,
    required this.updater,
    required this.child,
    Key? key,
    this.shouldUpdate,
  }) : super(key: key);

  factory StateUpdateDelegate.context({
    required S state,
    required BuildContext context,
    required Widget child,
    ShouldUpdate<T>? shouldUpdate,
    Key? key,
  }) {
    return StateUpdateDelegate(
      key: key,
      state: state,
      updater: () => (context as Element?)?.markNeedsBuild(),
      shouldUpdate: shouldUpdate,
      child: child,
    );
  }

  final S state;
  final Updater updater;
  final Widget child;
  final ShouldUpdate<T>? shouldUpdate;

  @override
  State<StateUpdateDelegate<T, S>> createState() =>
      _StateUpdateDelegateState<T, S>();
}

class _StateUpdateDelegateState<T, S extends AbstractState<T>>
    extends State<StateUpdateDelegate<T, S>> with SingleStateUpdateMixin {
  @override
  Widget build(BuildContext context) => widget.child;

  @override
  Updater get updater => widget.updater;

  @override
  AbstractState get state => widget.state;

  @override
  bool shouldUpdate(newValue, oldValue) {
    return widget.shouldUpdate?.call(newValue, oldValue) ?? true;
  }
}

mixin SingleStateUpdateMixin<W extends StatefulWidget> on State<W> {
  late final AbstractState _state;
  late final Updater _updater;
  dynamic _oldValue;

  AbstractState get state;

  bool shouldUpdate(newValue, oldValue);

  void _setStateValue(AbstractState state) {
    _state = state;
    state.addUpdater(update);
    _oldValue = state.value;
  }

  @override
  void initState() {
    super.initState();
    _updater = updater;
    _setStateValue(state);
  }

  @override
  void dispose() {
    _state.removeUpdater(update);
    super.dispose();
  }

  @override
  void didUpdateWidget(W oldWidget) {
    if (_state != state) {
      _state.removeUpdater(update);
      _setStateValue(state);
    }
    super.didUpdateWidget(oldWidget);
  }

  Updater get updater => _update;

  void update() {
    final newValue = _state.value;
    if (shouldUpdate(newValue, _oldValue)) {
      _updater();
      if (kDebugMode) {
        print('\'${widget.runtimeType}\' call updater');
      }
    }
    _oldValue = newValue;
  }

  void _update() {
    // (context as Element?)?.markNeedsBuild();
    if (mounted) setState(() {});
  }
}

class StatesUpdateBuilder extends StatefulWidget {
  const StatesUpdateBuilder({
    required this.states,
    required this.builder,
    this.extraUpdater,
    Key? key,
  }) : super(key: key);

  final List<AbstractState> states;
  final WidgetBuilder builder;
  final Updater? extraUpdater;

  @override
  State createState() => _StatesUpdateBuilderState();
}

class _StatesUpdateBuilderState extends State<StatesUpdateBuilder>
    with StatesUpdateMixin {
  @override
  Widget build(BuildContext context) => widget.builder(context);

  @override
  bool shouldUpdate(states) => true;

  @override
  List<AbstractState> get states => widget.states;

  @override
  void _update() {
    // (context as Element?)?.markNeedsBuild();
    if (mounted) setState(() {});
    widget.extraUpdater?.call();
  }
}

mixin StatesUpdateMixin<W extends StatefulWidget> on State<W> {
  late final List<AbstractState> _states;
  late final Updater _updater;

  List<AbstractState> get states;

  bool shouldUpdate(states);

  @override
  void initState() {
    super.initState();
    _updater = updater;
    _states = states;
    for (final state in _states) {
      state.addUpdater(update);
    }
  }

  @override
  void dispose() {
    for (final state in _states) {
      state.removeUpdater(update);
    }
    super.dispose();
  }

  @override
  void didUpdateWidget(W oldWidget) {
    if (_states != states) {
      for (final state in _states) {
        state.removeUpdater(update);
      }
      _states = states;
      for (final state in _states) {
        state.addUpdater(update);
      }
    }
    super.didUpdateWidget(oldWidget);
  }

  void update() {
    if (shouldUpdate(_states)) {
      _updater();
      if (kDebugMode) {
        print('\'${widget.runtimeType}\' call updater');
      }
    }
  }

  Updater get updater => _update;

  void _update() {
    // (context as Element?)?.markNeedsBuild();
    if (mounted) setState(() {});
  }
}
